<template>
  <div 
    ref="tooltipNode"
    class="lwq-tooltip" 
    v-on="outerEvents">
      <div 
        class="lwq-tooltip__trigger"
        ref="triggerNode"
        v-on="events">
        <slot></slot>
      </div>
      <Transition :name="transition">
        <div 
          v-if="isOpen"
          class="lwq-tooltip__popper"
          ref="popperNode">
          <slot name="content">{{ content }}</slot>
          <div id="arrow" data-popper-arrow></div>
        </div>
      </Transition>
  </div>
</template>
<script setup lang="ts">
import { computed, reactive, ref, watch } from 'vue';
import type { TooltipEmits, TooltipInstance, TooltipProps } from './types';
import { createPopper, type Instance } from '@popperjs/core';
import useClickOutside from '@/hooks/useClickOutside';
import { debounce } from 'lodash-es';

defineOptions({
  name: 'LwqTooltip'
});
const props = withDefaults(defineProps<TooltipProps>(), {
  placement: 'bottom',
  trigger: 'hover',
  transition: 'fade',
  openDelay: 0,
  closeDelay: 0,
})
const emits = defineEmits<TooltipEmits>();
const isOpen = ref<boolean>(false);
const triggerNode = ref<HTMLElement>();
const popperNode = ref<HTMLElement>();
const tooltipNode = ref<HTMLElement>();
let popperInstance: null | Instance = null;
let events: Record<string, (open?: boolean) => void> = reactive({});
let outerEvents: Record<string, (open?: boolean) => void> = reactive({});

const open = () => {
  isOpen.value = true;
  emits('visibleChange', true);
};

const close = () => {
  isOpen.value = false;
  emits('visibleChange', false);
};

// 防抖
const openDebounce = debounce(open, props.openDelay);
const closeDebounce = debounce(close, props.closeDelay);

const openFinal = () => {
  closeDebounce.cancel()
  openDebounce()
}
const closeFinal = () => {
  openDebounce.cancel()
  closeDebounce()
}

const togglePopper = () => {
  if (isOpen.value) {
    closeFinal();
  } else {
    openFinal();
  }
};

const attachEvents = () => {
  if (props.trigger === 'hover') {
    events['mouseenter'] = openFinal;
    outerEvents['mouseleave'] = closeFinal;
  } else if (props.trigger === 'click') {
    events['click'] = togglePopper;
  }
}
const popperOptions = computed(() => {
  return {
    placement: props.placement,
    modifiers: [
      {
        name: 'offset',
        options: {
          offset: [0, 9],
        },
      }
    ],
    ...props.popperOptions // 优先级更高
  }
})
useClickOutside(tooltipNode, () => {
  if (props.trigger === 'click' && isOpen.value && !props.manual) {
    togglePopper();
  }
})
// 设置手动触发时，click和hover不生效
if (!props.manual) {
  attachEvents();
}
watch(() => props.trigger, (newVal, oldVal) => {
  if (newVal !== oldVal) {
    events = {};
    outerEvents = {};
    attachEvents();
  }
});

watch(isOpen, (newVal) => {
  if (newVal) {
    if (triggerNode.value && popperNode.value) {
      popperInstance = createPopper(triggerNode.value, popperNode.value, popperOptions.value)
    } else {
      popperInstance?.destroy();
    }
  }
}, { flush: 'post' });

defineExpose<TooltipInstance>({
  open: openFinal,
  close: closeFinal,
})
</script>
<style scoped>
@import './style.css';
</style>